/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package argtrust.argtrustgraph;

import argtrust.measure.Measure;
import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;

import aima.core.logic.fol.parsing.ast.Sentence;

//import edu.cuny.argtrust.argsys.ArguingNode;
//import edu.cuny.argtrust.argsys.Argument;
//import edu.cuny.argtrust.argsys.ArguingLink;
//import edu.cuny.argtrust.argsys.DefeatPoint;
//import edu.cuny.argtrust.argsys.Defeat;
import argtrust.argsys.*;
import argtrust.trustnet.*;
import argtrust.logic.InferenceNode;
import argtrust.logic.RuleInferenceNode;
import argtrust.logic.FactInferenceNode;
import argtrust.belief.BeliefBase;
import argtrust.belief.Belief;
import argtrust.belief.ITrustBeliefSemantics;
import argtrust.argsys.IBeliefArgumentSemantics;
import argtrust.argsys.IArgumentationSemantics;

import argtrust.argsys.ArgumentationSystem;
import argtrust.out.graphviz.GVZArgTrustDrawing;
import argtrust.out.graphviz.GVZDot2TexAttributeFactory;

/**
 *
 * @author Yuqing Tang
 */
public class ArgTrustGraph {

    Set<Object> mReachable = new LinkedHashSet<Object>();
    IArgTrustDrawing mDrawing;
//    GraphIDSystem mIDSystem = new GraphIDSystem();
//    GVZGraph mGraph = new GVZGraph();
    IGraphAttributeFactory mAttributesFactory;
    //
    PrintWriter mOut;
    //
    ArgumentationSystem mArgSys;
    BeliefBase mBeliefBase;
    TrustNet mTrustNet;
    ITrustSemantics mTrustSemantics;
    ITrustBeliefSemantics mTrustBeliefSemantics;
    IBeliefArgumentSemantics mArgumentBeliefSemantics;
    IArgumentationSemantics mArgumentationSemantics;
    //
    TrustNode mRoot;

    public ArgTrustGraph() {
        mAttributesFactory = new GVZDot2TexAttributeFactory(this);
        mDrawing = new GVZArgTrustDrawing(this);
    }

    public void setDrawingOutput(
            IArgTrustDrawing drawing,
            IGraphAttributeFactory attribute_factory) {
        mDrawing = drawing;
        mAttributesFactory = attribute_factory;
    }

    public IGraphAttributeFactory getAttributesFactory() {
        return mAttributesFactory;
    }

    public void setAttributesFactory(IGraphAttributeFactory attributes_factory) {
        this.mAttributesFactory = attributes_factory;
    }

    public ITrustBeliefSemantics getTrustBeliefSemantics() {
        return mTrustBeliefSemantics;
    }

    public IBeliefArgumentSemantics getArgumentBeliefSemantics() {
        return mArgumentBeliefSemantics;
    }

    public TrustNode getRoot() {
        return mRoot;
    }

    public void setArgSys(ArgumentationSystem arg_sys) {
        this.mArgSys = arg_sys;
    }

    public void setBeliefBase(BeliefBase belief_base) {
        this.mBeliefBase = belief_base;
    }

    public void setTrustNet(TrustNet trust_net) {
        this.mTrustNet = trust_net;
    }

    public void setTrustSemantics(ITrustSemantics trust_semantics) {
        mTrustSemantics = trust_semantics;
    }

    public void setTrustBeliefSemantics(ITrustBeliefSemantics trust_belief_semantics) {
        mTrustBeliefSemantics = trust_belief_semantics;

    }

    public void setArgumentBeliefSemantics(IBeliefArgumentSemantics argument_belief_semantics) {
        mArgumentBeliefSemantics = argument_belief_semantics;

    }

    public IArgumentationSemantics getArgumentationSemantics() {
        return mArgumentationSemantics;
    }

    public void setArgumentationSemantics(IArgumentationSemantics argumentation_semantics) {
        this.mArgumentationSemantics = argumentation_semantics;
    }

    void drawInfNode(Argument arg, InferenceNode inf_node) {
        drawInfNode_(arg, inf_node);
        inf_node.unmarkHierarchy();
    }

    /**
     * Mark the inference nodes that can not be reached by the root agent
     * assuming the trust net has been explored and drawn.
     * TODO: Improve efficiency by better organizing the belief base.
     * @param arg
     * @param inf_node
     */
    boolean markUnreachableInfNode_(InferenceNode inf_node) {
        if (inf_node.isMark()) {
            return false;
        }
        inf_node.mark();
        if (inf_node instanceof FactInferenceNode) {
            FactInferenceNode fact_node = (FactInferenceNode) inf_node;
            Collection<Belief> belief_set = mBeliefBase.getBeliefSet(fact_node.getFactIterator().get());
            if (belief_set != null) {
                for (Belief belief : belief_set) {
                    if (isReachable(belief.getOwner())) {
                        inf_node.unmark();
                        return true;
                    }
                }
            }
        }
        boolean flag = false;
        if (inf_node instanceof RuleInferenceNode) {
            RuleInferenceNode rule_node = (RuleInferenceNode) inf_node;
            Collection<Belief> belief_set = mBeliefBase.getBeliefSet(rule_node.getRuleIterator().get());
            if (belief_set != null) {
                for (Belief belief : belief_set) {
                    if (isReachable(belief.getOwner())) {
                        flag = true;
                        break;
                    }
                }
            }
            if (flag == true) {
                for (InferenceNode sub_inf : rule_node.getChildrenList()) {
                    flag = flag && markUnreachableInfNode_(sub_inf);
                }

            }
        }
        if (flag == true) {
            inf_node.unmark();

        }
        return flag;
    }

    /**
     * Draw the inference node.
     * TODO: Draw only the inference node that can be reached by the query agent.
     * @param arg
     * @param inf_node
     */
    void drawInfNode_(Argument arg, InferenceNode inf_node) {
        if (inf_node.isMark()) {
            return;
        }
        inf_node.mark();
        IGraphAttribute property;
        if (inf_node instanceof FactInferenceNode) {
            FactInferenceNode fact_node = (FactInferenceNode) inf_node;
            if (inf_node == arg.getEndInferenceNode()) {
                property = mAttributesFactory.getFinalConclusionProperties(arg, fact_node);
            } else {
                property = mAttributesFactory.getPremiseProperties(arg, fact_node);
            }
            mDrawing.drawFactNode(arg, fact_node, property);


        } else if (inf_node instanceof RuleInferenceNode) {
            RuleInferenceNode rule_node = (RuleInferenceNode) inf_node;
            property = mAttributesFactory.getInferenceRuleAttributes(arg, rule_node);
            mDrawing.drawRuleNode(arg, rule_node, property);
        }

        if (inf_node instanceof RuleInferenceNode) {
            RuleInferenceNode rule_node = (RuleInferenceNode) inf_node;
            IGraphAttribute conclusion_properties;
            if (rule_node == arg.getEndInferenceNode()) {
                conclusion_properties = mAttributesFactory.getFinalConclusionProperties(arg, rule_node);
            } else {
                conclusion_properties = mAttributesFactory.getIntermediaConclusionProperties(arg, rule_node);
            }
            mDrawing.drawRuleConclusionNode(arg, rule_node, conclusion_properties);

            for (InferenceNode sub_inf : rule_node.getChildrenList()) {
                drawInfNode_(arg, sub_inf);
            }
        }
    }

    void drawBeliefEdge(Argument arg, Object obj, Belief belief) {
        if (!isReachable(belief.getOwner())) {
            return;
        }
        IGraphAttribute attributes = mAttributesFactory.getDirectBeliefAttributes(arg, belief);
        mDrawing.drawBeliefEdge(belief.getOwner(), arg, obj, attributes);
//        for (TrustNode trust_node : mStartTrustCollection) {
        Agent agent = mRoot.getAgent();
        Agent other = belief.getOwner();
        if (agent.equals(other)) {
            return;
        }
        Measure tr = mTrustSemantics.trust(agent, other);
        if (tr instanceof IndirectTrust) {
            IGraphAttribute trust_attributes = mAttributesFactory.getIndirectTrustAttributes(arg, (IndirectTrust) tr);
            mDrawing.drawTrustLink(agent, other, trust_attributes);
        }

//        }
    }

    void drawInfEdges(Argument arg, InferenceNode inf_node) {
        drawInfEdges_(arg, inf_node);
        inf_node.unmarkHierarchy();
    }

    /**
     * Draw the inference edge.
     * TODO: Draw only the inference node that can be reached by the query agent.
     * @param arg
     * @param inf_node
     */
    void drawInfEdges_(Argument arg, InferenceNode inf_node) {
        if (inf_node.isMark()) {
            return;
        }
        inf_node.mark();
        if (inf_node instanceof RuleInferenceNode) {
            RuleInferenceNode rule_node = (RuleInferenceNode) inf_node;
            mDrawing.drawInfEdge(arg,
                    rule_node.getRuleInstance(),
                    rule_node.getRuleInstance().getConclusion(),
                    this.mAttributesFactory.getAttribute());

            for (InferenceNode sub_inf : rule_node.getChildrenList()) {
                mDrawing.drawInfEdge(arg,
                        conclusionNode(sub_inf),
                        rule_node.getRuleInstance(),
                        this.mAttributesFactory.getAttribute());

                drawInfEdges_(arg, sub_inf);

            }
            Collection<Belief> belief_set = mBeliefBase.getBeliefSet(rule_node.getRuleIterator().get());
            if (belief_set != null) {
                for (Belief belief : belief_set) {
                    drawBeliefEdge(arg, rule_node.getRuleInstance(), belief);
                }
            }

        } else if (inf_node instanceof FactInferenceNode) {
            FactInferenceNode fact_node = (FactInferenceNode) inf_node;
            Collection<Belief> belief_set = mBeliefBase.getBeliefSet(fact_node.getFactIterator().get());
            if (belief_set != null) {
                for (Belief belief : belief_set) {
                    drawBeliefEdge(arg, fact_node.getFactInstance(), belief);
                }
            }


        }

    }

    void drawArgumentNodes(ArguingNode arg_node) {
        if (arg_node == null) {
            return;
        }
        drawArgumentNodes_(arg_node);
        arg_node.unmarkHierarchy();
    }

    void drawArgumentNodes_(ArguingNode arg_node) {
        if (arg_node.isMark()) {
            return;
        }
        Argument arg = arg_node.getArgument();
        arg_node.mark();

        markUnreachableInfNode_(arg.getEndInferenceNode());
        if (arg.getEndInferenceNode().isMark()) {
            return;
        }
        markReachable(arg);

        mDrawing.startArgument(arg, mAttributesFactory);
        drawInfNode(arg, arg.getEndInferenceNode());
        drawInfEdges(arg, arg.getEndInferenceNode());
        mDrawing.endArgument();
        if (arg_node.getArguingLinkList() == null) {
            return;
        }
        for (ArguingLink arg_link : arg_node.getArguingLinkList()) {
            ArguingNode next_node = arg_link.getDefeaterNode();
            drawArgumentNodes_(next_node);
        }
    }

    void drawDefeatEdges(ArguingNode arg_node) {
        if (arg_node == null) {
            return;
        }
        drawDefeatEdges_(arg_node);
        arg_node.unmarkHierarchy();
    }

    Object conclusionNode(InferenceNode inf) {
        if (inf instanceof RuleInferenceNode) {
            return ((RuleInferenceNode) inf).getRuleInstance().getConclusion();

        } else {
            return ((FactInferenceNode) inf).getFactInstance();
        }
    }

    void drawDefeatEdges_(ArguingNode arg_node) {
        if (arg_node == null) {
            return;
        }
        if (arg_node.isMark()) {
            return;
        }
        Argument arg = arg_node.getArgument();
        arg_node.mark();

        markUnreachableInfNode_(arg.getEndInferenceNode());
        if (arg.getEndInferenceNode().isMark()) {
            return;
        }


        if (arg_node.getArguingLinkList() == null) {
            return;
        }
        for (ArguingLink arg_link : arg_node.getArguingLinkList()) {

            ArguingNode next_node = arg_link.getDefeaterNode();
            if (!isReachable(next_node.getArgument())) {
                continue;
            }

            IGraphAttribute attributes = mAttributesFactory.getDefeatAttributes(arg_link.getDefeat());
            mDrawing.drawDefeatEdge(arg_link.getDefeat(), attributes);

            drawDefeatEdges_(next_node);
        }


    }

    void startTrustnet() {
        mDrawing.startTrustnet(mAttributesFactory);


    }

    void endTrustnet() {
        mDrawing.endTrustnet();
    }

    void markReachable(Agent agent) {
        mReachable.add(agent);
    }

    void markReachable(Argument argument) {
        mReachable.add(argument);
    }

    boolean isReachable(Agent agent) {
        return mReachable.contains(agent);
    }

    boolean isReachable(Argument argument) {
        return mReachable.contains(argument);
    }

//    void markReachable(Agent agent)
//    {
//        mIDSystem.getID(agent);
//    }
    void drawAgent(Agent agent) {


        IGraphAttribute property = this.mAttributesFactory.getAgentAttribute(agent);
        mDrawing.drawAgent(agent, property);
    }

    void drawTrustNode(TrustNode trust_node) {
        drawTrustNode_(trust_node);
        trust_node.unmarkHierarchy();
    }

    void drawTrustNode_(TrustNode trust_node) {
        if (trust_node == null) {
            return;
        }
        if (trust_node.isMark()) {
            return;
        }
        Agent agent = trust_node.getAgent();
        trust_node.mark();
        markReachable(agent);
        drawAgent(agent);
        for (TrustLink trust_link : trust_node.getTrustLinkList()) {
            drawTrustNode_(trust_link.getTrusteeNode());
        }

    }

    void drawTrustLink(TrustNode trust_node) {
        drawTrustLink_(trust_node);
        trust_node.unmarkHierarchy();
    }

    void drawTrustLink_(TrustNode trust_node) {
        if (trust_node == null) {
            return;
        }
        if (trust_node.isMark()) {
            return;
        }
        Agent agent = trust_node.getAgent();
        if (!isReachable(agent)) {
            return;
        }
        trust_node.mark();

        for (TrustLink trust_link : trust_node.getTrustLinkList()) {
            Agent other = trust_link.getTrusteeNode().getAgent();
//            TrustLevel tr = mTrustSemantics.trust(agent, other);
            if (isReachable(other)) {
                IGraphAttribute attributes = mAttributesFactory.getTrustLinkAttributes(trust_link);
//                IGraphAttribute attributes = new IGraphAttribute(String.valueOf(tr.getTrustLevel()));
//                if (tr instanceof IndirectTrust)
//                {
//                    attributes.addAttribute("style", "dashed");
//                }
                mDrawing.drawTrustLink(agent, other, attributes);
                drawTrustLink_(trust_link.getTrusteeNode());
            }
        }

    }

    public void openFile(String file_name) {
        File out_file = new File(file_name);
        openFile(out_file);

    }

    public void openFile(File out_file) {
        try {
            mOut = new PrintWriter(new FileOutputStream(out_file));
            mDrawing.setOut(mOut);

        } catch (IOException e1) {
            e1.printStackTrace();
            /*handler required but null */
        }


    }

    public java.io.PrintWriter getOutputWriter() {
        return mOut;
    }

    public void close() {
        if (mOut != null) {
            mOut.close();
        }
    }

    public void draw(ArguingNode arg_node,
            TrustNode trust_node) {
        mRoot = trust_node;

        mDrawing.start(mAttributesFactory);


        if (mRoot != null) {
            startTrustnet();
            drawTrustNode(trust_node);
            drawTrustLink(trust_node);
            endTrustnet();
        }
        drawArgumentNodes(arg_node);
        drawDefeatEdges(arg_node);

        mDrawing.end();

    }

    public void draw(
            Collection<ArguingNode> arg_nodes,
            TrustNode root) {
//        mStartTrustCollection = trust_nodes;
        mRoot = root;
        mDrawing.start(mAttributesFactory);
        if (mRoot != null) {
            startTrustnet();
            drawTrustNode_(mRoot);
            mRoot.unmarkHierarchy();
            drawTrustLink_(mRoot);
            mRoot.unmarkHierarchy();

            endTrustnet();
        }
        if (arg_nodes != null) {
            for (ArguingNode arg_node : arg_nodes) {
                drawArgumentNodes_(arg_node);

            }
            for (ArguingNode arg_node : arg_nodes) {
                arg_node.unmarkHierarchy();
            }

            for (ArguingNode arg_node : arg_nodes) {
                drawDefeatEdges_(arg_node);
            }
            for (ArguingNode arg_node : arg_nodes) {
                arg_node.unmarkHierarchy();
            }
        }
        mDrawing.end();
    }
    //    public static void main(String args[]) {
//        ArgTrustGraph arg_graph = new ArgTrustGraph("out.dot");
//
//
//    }
}
